/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.core.action.retrieve;

import com.inspur.edp.bef.api.action.assembler.IBEActionAssembler;
import com.inspur.edp.bef.api.be.IBEContext;
import com.inspur.edp.bef.api.exceptions.BefDataPermissionDeniedException;
import com.inspur.edp.bef.api.parameter.retrieve.RespectiveRetrieveResult;
import com.inspur.edp.bef.api.parameter.retrieve.RetrieveParam;
import com.inspur.edp.bef.core.action.AuthorityUtil;
import com.inspur.edp.bef.core.action.base.ActionUtil;
import com.inspur.edp.bef.core.be.CoreBEContext;
import com.inspur.edp.bef.core.lock.LockService;
import com.inspur.edp.bef.core.lock.LockUtils;
import com.inspur.edp.bef.spi.action.AbstractAction;
import com.inspur.edp.bef.spi.action.assembler.retrieve.RetrieveActionAssembler;
import com.inspur.edp.cef.api.repository.IRootRepository;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.InnerUtil;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.Util;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.entity.IMultiLanguageData;
import java.util.Date;
import java.util.Map;

public class RetrieveAction extends AbstractAction<RespectiveRetrieveResult> {

	//region Constructor

	public RetrieveAction(IBEContext beContext, RetrieveParam para) {
		this.para = para;
	}

	//endregion Constructor

	//region 字段属性

	private RetrieveParam para;

	//endregion 字段属性

	//region override

	@Override
	public final void execute() {
		// AuthorityUtil.checkDataAuthority("Retrieve",this.getBEContext());
		setResult(new RespectiveRetrieveResult());
		CoreBEContext beContext = ActionUtil.getBEContext(this);
		if (!beContext.isDeleted()) {
			//无版本字段时才按照getForceFromRepository取数
			if (para.getForceFromRepository() && !beContext.isNew() && beContext.hasData() && !beContext.hasChange() && !beContext.isLocked()) {
				beContext.clearData();
			}
			boolean islocked = beContext.isLocked();
			if (para.getNeedLock() && !islocked) //需要加锁
			{
				getResult().setLockFailed(!addLock(beContext));
				//Result.LockFailedIds.add(beContext.ID);
			}
			afterHasLock(beContext, islocked);
		}
		setRetrieveResult(ActionUtil.getBEContext(this));
	}

	@Override
	protected final IBEActionAssembler getAssembler() {
		return GetAssemblerFactory().getRetrieveActionAssembler(ActionUtil.getBEContext(this), para);
	}
	//endregion override

	//region private

	/** 成功加锁后@param beContext*/
	private void afterHasLock(CoreBEContext beContext, boolean islocked) {
		if (beContext.isNew()) {
			return;
		}
		if (beContext.hasData()) {
			if (islocked || beContext.hasChange()) {
				return;
			}
			//① 已有数据，检查版本；
			checkDataVersion(beContext);
		} else {
			//② 无数据，检索数据；
			retrieveFromDAL(beContext);
		}
	}

	/**
	 * 检查版本
	 *
	 * @param beContext
	 */
	private void checkDataVersion(CoreBEContext beContext) {
		Date currentVersion = getVersionFromDal(beContext);
			if (currentVersion!= null &&(currentVersion.compareTo(new Date(0)) == 0 || (beContext.getBizEntity().getVersionControlPropValue() != null
					&& currentVersion.compareTo(beContext.getBizEntity().getVersionControlPropValue()) == 0))) {
				//①若版本一致，则不需要作为；
				checkMultiLang(beContext);
				return;
			}
		//②若版本不一致，则根据dataID更新数据，更新版本；
		beContext.clearData();
		retrieveFromDAL(beContext);
	}

	private void checkMultiLang(CoreBEContext beContext) {
		if(para.getRetrieveFilter() == null || !para.getRetrieveFilter().isEnableMultiLanguage()) {
			return;
		}
		if(beContext.isMultiLanguaged()) {
			return;
		}
		IEntityData orgData = beContext.getOriginalData();
		if (orgData == null || !(orgData instanceof IMultiLanguageData)
				|| !((IMultiLanguageData) orgData).getMultiLanguageInfos().isEmpty()) {
			return;
		}
		IEntityData data = retrieveFromDAL(beContext.getID());
		if (data == null) {
			return;
		}
		mergeMultiLanguage(data, orgData, null);

		IEntityData tranData = beContext.getTransactionData();
		if(tranData != null) {
			mergeMultiLanguage(data, tranData, beContext.getTransactionalChange());
		}

		IEntityData currData = beContext.getCurrentData();
		if(currData != null) {
			beContext.suspendChangeListener();
			try {
				mergeMultiLanguage(data, currData, beContext.getCurrentTemplateChange());
			}finally {
				beContext.resumeChangeListener();
			}
		}
		beContext.setMultiLanguaged(true);
//		((IMultiLanguageData) orgData).getMultiLanguageInfos().putAll(((IMultiLanguageData) data).getMultiLanguageInfos());
//		IMultiLanguageData tranData = (IMultiLanguageData)beContext.getTransactionData();
//		if(tranData != null){
//			tranData.getMultiLanguageInfos().putAll(((IMultiLanguageData) data).getMultiLanguageInfos());
//			IChangeDetail change = beContext.getTransactionalChange();
//			if(change != null && change instanceof ModifyChangeDetail && !((ModifyChangeDetail) change).getMultiLanguageInfos().isEmpty()) {
//				InnerUtil.mergeMultiLanguageInfoToChanges(((ModifyChangeDetail) change).getMultiLanguageInfos(),
//						tranData.getMultiLanguageInfos(), ((ModifyChangeDetail) change).getPropertyChanges());
//			}
//		}
//
//		IMultiLanguageData currData = (IMultiLanguageData)beContext.getCurrentData();
//		if(currData != null){
//			currData.getMultiLanguageInfos().putAll(((IMultiLanguageData) data).getMultiLanguageInfos());
//			IChangeDetail change = beContext.getCurrentTemplateChange();
//			if(change != null && change instanceof ModifyChangeDetail && !((ModifyChangeDetail) change).getMultiLanguageInfos().isEmpty()) {
//				InnerUtil.mergeMultiLanguageInfoToChanges(((ModifyChangeDetail) change).getMultiLanguageInfos(),
//						currData.getMultiLanguageInfos(), ((ModifyChangeDetail) change).getPropertyChanges());
//			}
//		}
	}

	private void mergeMultiLanguage(IEntityData src, IEntityData target, IChangeDetail change) {
		InnerUtil.resetMultiLangInfo((IMultiLanguageData)src, (IMultiLanguageData)target);
		if(change != null && change instanceof ModifyChangeDetail) {
			InnerUtil.mergeMultiLanguageInfoToChanges(((ModifyChangeDetail) change).getMultiLanguageInfos(),
					((IMultiLanguageData)target).getMultiLanguageInfos(), ((ModifyChangeDetail) change).getPropertyChanges());
		}
		if(src.getChilds() != null) {
			for (Map.Entry<String, IEntityDataCollection> childCollection : src.getChilds().entrySet()) {
				if(childCollection.getValue() == null || childCollection.getValue().isEmpty()) {
					continue;
				}
				IEntityDataCollection targetChildColl = target.getChilds().get(childCollection.getKey());
				if(targetChildColl == null || targetChildColl.isEmpty()) {
					continue;
				}
				for (IEntityData childData : childCollection.getValue()) {
					IEntityData targetChildData = targetChildColl.tryGet(childData.getID());
					if (targetChildData == null) {
						continue;
					}
					IChangeDetail childChange = change != null ? Util
							.getChildChange(change, childCollection.getKey(), childData.getID()) : null;
					mergeMultiLanguage(childData, targetChildData, childChange);
				}
			}
		}
	}

	private Date getVersionFromDal(IBEContext beContext) {
		IRootRepository repository = ActionUtil.getRootEntity(this).getBEContext()
				.getSessionItem().getBEManager().getRepository();
		return repository.getVersionControlValue(beContext.getID());
	}

	private void retrieveFromDAL(IBEContext beContext) {
		IEntityData data = retrieveFromDAL(beContext.getID());

		if (data == null) {
			return;
		}
		//if (!DemandRetrievePermission(data))
		//    return;
		//加载到BEContext
		ActionUtil.getRootEntity(this).loadData(data);
		((CoreBEContext)beContext).setMultiLanguaged(
				para.getRetrieveFilter() != null && para.getRetrieveFilter().isEnableMultiLanguage());
		try {
			AuthorityUtil.checkDataAuthority(beContext);
		} catch (Throwable e) {
			ActionUtil.getBEContext(this).clearData();
			throw e;
		}

		//执行AfterRetrieve时机Determination
		ActionUtil.getRootEntity(this).afterLoadingDeterminate();
	}

	/**
	 * DAL检索数据
	 *
	 * @param id 唯一标识
	 */
	protected final IEntityData retrieveFromDAL(String id) {
		IRootRepository repository = ActionUtil.getRootEntity(this).getBEContext().getSessionItem()
				.getBEManager().getRepository();

			if (para.getNodeSortInfos() != null && !para.getNodeSortInfos().isEmpty()) {
				return repository.retrieve(id, para.getNodeSortInfos(),para.getRetrieveFilter());
			}
			return repository.retrieve(id,null,para.getRetrieveFilter());
	}

	/** 加锁，返回是否成功*/
	private boolean addLock(IBEContext beContext) {
		if(!LockUtils.needLock(beContext.getModelResInfo()))
			return true;
		LockService.getInstance().addLock(beContext.getBizEntity().getBEType(), beContext.getID());
		return beContext.isLocked();
	}

	private void setRetrieveResult(IBEContext beContext) {
		getResult().setData(RetrieveUtils.getData(beContext, para.getCacheType()));
	}

	private boolean demandRetrievePermission(IEntityData data) {
    RetrieveActionAssembler assembler = (RetrieveActionAssembler) getAssembler();
		return assembler != null && assembler.demandDataPermission(data);
	}
	//endregion
}
